#include "p4.h"
#include <iostream>
#include <cmath>
#include <random>


bool isPrime(int n) {
    if (n < 2) {
        return false;
    }

    if (n == 2 || n == 3) {
        return true;
    }

    if (n % 2 == 0 || n % 3 == 0) {
        return false;
    }

    int sqrt_n = static_cast<int>(std::sqrt(n));
    for (int i = 5; i <= sqrt_n; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }

    return true;
}


// Modular exponentiation
int modPow(int base, int exp, int mod) {
    int result = 1;
    base = base % mod; // Update base if it is more than mod
    while (exp > 0) {
        if (exp % 2 == 1)  // If exp is odd, multiply base with the result
            result = (result * base) % mod;
        exp = exp >> 1;  // exp = exp / 2
        base = (base * base) % mod;  // Change base to base^2 % mod
    }
    return result;
}


bool millerTest(int d, int n) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dis(2, n - 2);
    int a = dis(gen);

    int x = modPow(a, d, n);;
    if (x == 1 || x == n - 1) {
        return true;
    }

    while (d != n - 1) {
        x = (x * x) % n;
        d *= 2;

        if (x == 1) {
            return false;
        }

        if (x == n - 1) {
            return true;
        }
    }

    return false;
}


bool isPrimeByMillerRabinAlg(int n, int k) {
    if (n <= 1 || n == 4) {
        return false;
    }

    if (n <= 3) {
        return true;
    }

    int d = n - 1;
    while (d % 2 == 0) {
        d /= 2;
    }

    for (int i = 0; i < k; i++) {
        if (!millerTest(d, n)) {
            return false;
        }
    }

    return true;
}


void testHomeWork4_10() {
    std::cout << " Please input a integer(0~9): ";
    int num {};
    std::cin >> num;

    if (std::cin.fail()) {
        std::cin.clear(); // Clear error flag
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Ignore incorrect input left in the stream
        std::cout << "Invalid input. Please enter a valid number.\n";
    } else {
        std::cout << " You entered integer is " << num << '\n';
    }

    if (isPrime(num)) {
        std::cout << " This digital is prime" << std::endl;
    } else {
        std::cout << " This digital is not prime" << std::endl;
    }

    if (isPrimeByMillerRabinAlg(num, 5)) {
        std::cout << " This digital is prime, by Miller-Rabin algorithim" << std::endl;
    } else {
        std::cout << " This digital is not prime, by Miller-Rabin algorithim" << std::endl;
    }
}


void testHomeWork4_12() {
    std::cout << " Please input an character(a ~ z, A ~ Z): ";
    char ch {};
    std::cin >> ch;
    int acsiiVar {ch};
    if (std::cin.fail()) {
        std::cin.clear(); // Clear error flag
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Ignore incorrect input left in the stream
        std::cout << "Invalid input. Please enter a valid character.\n";
    } else {
        std::cout << " You entered character is \'" << ch << "\', ASCII Code is " << static_cast<int>(ch) << '\n';
        std::cout << " You entered character is \'" << ch << "\', ASCII Code is " << acsiiVar << '\n';
    }
}


double getDoubleDecimal() {
    std::cout << " Please input decimal variable: ";
    double var {};
    std::cin >> var;
    if (std::cin.fail()) {
        std::cin.clear(); // Clear error flag
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Ignore incorrect input left in the stream
        std::cout << "Invalid input. Please enter a valid decimal vaiable.\n";
    }

    return var;
}


char getCharacterOperator() {
    std::cout << " Please input operator( +, -, *, / ) symbol character: ";
    char ch {};
    std::cin >> ch;
    if (std::cin.fail()) {
        std::cin.clear(); // Clear error flag
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Ignore incorrect input left in the stream
        std::cout << "Invalid input. Please enter a valid symbol.\n";
    }

    return ch;
}


void testHomeWork4_x() {

    double a = getDoubleDecimal();
    double b = getDoubleDecimal();
    char opCh = getCharacterOperator();


    if (opCh == '+') {
        std::cout << a << " + " << b << " = " << a + b << '\n';
    } else if (opCh == '-') {
        std::cout << a << " - " << b << " = " << a - b << '\n';
    } else if (opCh == '*') {
        std::cout << a << " * " << b << " = " << a * b << '\n';
    } else if (opCh == '/') {
        if (b != 0) {
            std::cout << a << " / " << b << " = " << a / b << '\n';
        } else {
            std::cout << " Error, The denominator of division is 0" << '\n';
        }
    }
}


double getTowerHeight() {
    std::cout << "Please enter the height of the tower in meters: ";
    double var {};
    std::cin >> var;
    if (std::cin.fail()) {
        std::cin.clear(); // Clear error flag
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Ignore incorrect input left in the stream
        std::cout << "Invalid input. Please enter a valid height(meters).\n";
    }

    return var;
}

double calBallHeight(int towerHeight, int curTime) {
    double dropedDis = 9.8 * std::pow(curTime, 2) / 2.;
    if (dropedDis > towerHeight) {
        dropedDis = towerHeight;
    }

    return towerHeight - dropedDis;
}


void testHomeWork4_x2() {
    double towerHeight = getTowerHeight();
    for (int curTime = 0; curTime <= 5; ++curTime) {
        if (calBallHeight(towerHeight, curTime) == 0.) {
            std::cout << "At " << curTime << " seconds, the ball is on the ground." << '\n';
        } else {
            std::cout << "At " << curTime << " seconds, the ball is at height: " << calBallHeight(towerHeight, curTime) << " meters" << '\n';
        }
    }
}
